/*
 * get the basic information in the ubi image and ubifs image
 * most same to the unubinize.c
 */
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <Windows.h>

#include "defs.h"
#include "mtd_swab.h"
#include "crc32.h"
#include "libubi.h"
#include "ubi-media.h"
#include "ubifs-media.h"
#include "ubifs.h"
#include "libubigen.h"
#include "unubinize.h"
#include "key.h"
#include "utils.h"

static void ubi_srand()
{
	uint64_t long_seed;
	int32_t seed;

	long_seed = GetTickCount();
	long_seed *= GetCurrentProcessId();
	seed = long_seed % RAND_MAX;

	srand(seed);

	return;
}

static int check_ubi_args(struct ubigen_info *u)
{
	if (u->peb_size < 0)
		return errmsg("physical eraseblock size was not specified\n");

	if (u->peb_size > 1024*1024)
		return errmsg("too high physical eraseblock size %d\n", u->peb_size);

	if (u->min_io_size < 0)
		return errmsg("min. I/O unit size was not specified\n");

	if (u->peb_size % u->min_io_size)
		return errmsg("physical eraseblock should be multiple of min. I/O units\n");

	if (u->vid_hdr_offs) {
		if (u->vid_hdr_offs + (int)UBI_VID_HDR_SIZE >= u->peb_size)
			return errmsg("bad VID header position");
		if (u->vid_hdr_offs % 8)
			return errmsg("VID header offset has to be multiple of min. I/O unit size\n");
	}

	return 0;
}

/**
 * validate_sb - validate superblock node.
 * @c: UBIFS file-system description object
 * @sup: superblock node
 *
 * This function validates superblock node @sup. Since most of data was read
 * from the superblock and stored in @c, the function validates fields in @c
 * instead. Returns zero in case of success and %-EINVAL in case of validation
 * failure.
 */
static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
{
	long long max_bytes;
	int err = 1, min_leb_cnt;

	if (!c->key_hash) {
		err = 2;
		goto failed;
	}

	if (sup->key_fmt != UBIFS_SIMPLE_KEY_FMT) {
		err = 3;
		goto failed;
	}


	if (c->log_lebs < UBIFS_MIN_LOG_LEBS ||
	    c->lpt_lebs < UBIFS_MIN_LPT_LEBS ||
	    c->orph_lebs < UBIFS_MIN_ORPH_LEBS ||
	    c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
		err = 4;
		goto failed;
	}

	/*
	 * Calculate minimum allowed amount of main area LEBs. This is very
	 * similar to %UBIFS_MIN_LEB_CNT, but we take into account real what we
	 * have just read from the superblock.
	 */
	min_leb_cnt = UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs;
	min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6;

	if (c->max_leb_cnt < min_leb_cnt) {
		errmsg("bad LEB count: %d in superblock, %d minimum required\n",
			  c->leb_cnt, min_leb_cnt);
		goto failed;
	}

	if (c->max_leb_cnt < c->leb_cnt) {
		errmsg("max. LEB count %d less than LEB count %d\n",
			  c->max_leb_cnt, c->leb_cnt);
		goto failed;
	}

	if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) {
		errmsg("too few main LEBs count %d, must be at least %d\n",
			  c->main_lebs, UBIFS_MIN_MAIN_LEBS);
		goto failed;
	}

	max_bytes = (long long)c->leb_size * UBIFS_MIN_BUD_LEBS;
	if (c->max_bud_bytes < max_bytes) {
		errmsg("too small journal (%lld bytes), must be at least %lld bytes\n",
			  c->max_bud_bytes, max_bytes);
		goto failed;
	}

	max_bytes = (long long)c->leb_size * c->main_lebs;
	if (c->max_bud_bytes > max_bytes) {
		errmsg("too large journal size (%lld bytes), only %lld bytes available in the main area\n",
			  c->max_bud_bytes, max_bytes);
		goto failed;
	}

	if (c->jhead_cnt < 1 || c->jhead_cnt > UBIFS_MAX_JHEADS) {
		err = 9;
		goto failed;
	}

	if (c->fanout < UBIFS_MIN_FANOUT ||
	    ubifs_idx_node_sz(c, c->fanout) > c->leb_size) {
		err = 10;
		goto failed;
	}

	if (UBIFS_SB_LEBS + UBIFS_MST_LEBS + c->log_lebs + c->lpt_lebs +
	    c->orph_lebs + c->main_lebs != c->max_leb_cnt) {
		err = 12;
		goto failed;
	}

	if (c->default_compr < 0 || c->default_compr >= UBIFS_COMPR_TYPES_CNT) {
		err = 13;
		goto failed;
	}

	if (c->rp_size < 0 || max_bytes < c->rp_size) {
		err = 14;
		goto failed;
	}

	if (le32_to_cpu(sup->time_gran) > 1000000000 ||
	    le32_to_cpu(sup->time_gran) < 1) {
		err = 15;
		goto failed;
	}

	return 0;

failed:
	errmsg("bad superblock, error %d\n", err);
	return -1;
}

int get_ubi_img_info(char *ubi_image_file, struct volume_info_node *vi_list_head, struct ubigen_info *u, struct ubifs_info *c)
{
	/* pebsize and lebsize of the ubi image */
	uint32_t peb_size;
	uint32_t leb_size;
	uint32_t peb_vh_offset;
	uint32_t peb_data_offset;
	uint32_t image_seq;
	
	/* buffer to store a temp file which will store a volume image */
	char temp_file[BUF_SIZE];
	/* buffer to store misc data */
	char buf[BUF_SIZE];
	/* after got the peb size, it's used to read the data in the peb */
	char *peb_buf = NULL;
	char *data_buf = NULL;
	
	HANDLE h_ubiimg;
	/* to remember the current reading peb offset */
	uint32_t current_peb_offs;

	/* some structs */
	struct ubi_ec_hdr ec_hdr;
	struct ubi_vid_hdr vid_hdr;
	struct ubi_vtbl_record vtbl_rec;
	struct volume_info_node *vi_node;
	struct leb_list *leb_node;

	DWORD read_len;
	/* current file pointer */
	DWORD current_offs;
	/* the size of the image file */
	uint32_t file_size;
	
	/* return value */
	int ret_val = -1;
	DWORD ret;

	/* ec header values in cpu's endian */
	/* int64_t eh_ec; */
	uint32_t eh_crc;
	uint32_t eh_vh_offset;
	uint32_t eh_data_offset;
	uint32_t eh_image_seq;
	
	/* vid header values in cpu's endian */
	uint32_t vh_vol_id;
	uint32_t vh_lnum;
	uint32_t vh_data_size;
	uint32_t vh_used_ebs;
	uint32_t vh_data_pad;
	uint32_t vh_data_crc;
	uint64_t vh_sqnum;
	uint32_t vh_crc;
	
	/* vtbl index to traverse the vtbl */
	uint32_t idx = 0;
	
	/* vtbl record values in cpu's endian */
	uint32_t vr_reserved_pebs;
	uint32_t vr_alignment;
	uint32_t vr_data_pad;
	uint16_t vr_name_len;
	uint32_t vr_crc;

	/**
	 * we remember the max volume id to 
	 * determine the minimum size of the volume table
	 */
	uint32_t max_vol_id = 0;

	/**
	 * Because the peb_size is not stored in the image,
	 * we need to get it through an other way.
	 * According to the ubinize program,
	 * we know that the volume table is resides in peb1 and peb2,
	 * which are at the beginning of the image.
	 * Because UBI_MAX_VOLUMES = 128,
	 * so we scan the first 128 ubi_vtbl_records in peb1,
	 * and caculate the total pebs the all volumes use.
	 * thus we can get the peb_size by the image size and total pebs.
	 * Note that the layout volume uses another two pebs.
	 * Something should be considered:
	 * 1. the peb_size may less than 128 * sizeof(ubi_vtbl_record) = 22016.
     * 2. the record count may less than 128, which mean that
	 *    we will read some meaningless record which may confuse us.
	 * for 1. we check the image size and crc value to ensure the data is valid.
	 * for 2. because the size of a exist volume can't be zero or less,
	 *        so the pebs which a volume uses can't be zero,
	 *        we can check the value of reserved_pebs.
	 * To ensure the correctness, we will calculate the crc all the time,
	 * and after we got the peb_size,
	 * we also compare the content of peb1 and peb2.
	 */

	/** read peb 1 & peb 2
	 * we can get the volume table,
	 * thus we can get the name of a volume
	 * and peb count that each volume uses
	 */

	/* read the first 64 bytes for ec_header */
	h_ubiimg = CreateFile(ubi_image_file, GENERIC_READ, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h_ubiimg == INVALID_HANDLE_VALUE) {
		errmsg("Open %s failed!: %s\n", ubi_image_file, last_error(msg_buf, BUF_SIZE));
		return -1;
	}
	
	/**
	 * Note: the values read out are big-endian
	 */
	if (!ReadFile(h_ubiimg, &ec_hdr, UBI_EC_HDR_SIZE, &read_len, NULL)
		|| read_len != UBI_EC_HDR_SIZE)
	{
		errmsg("Read %s ec header failed!: %s\n", ubi_image_file,
			last_error(msg_buf, BUF_SIZE));
		goto err_out;
	}
	
	/* Check the magic & version values */
	if (UBI_EC_HDR_MAGIC != be32_to_cpu(ec_hdr.magic)) {
		errmsg("EC header MAGIC is not correct!\n");
		goto err_out;
	}	
	if (ec_hdr.version > UBI_VERSION) {
		errmsg("can only support images under version %d!\n", UBI_VERSION);
		goto err_out;
	}

	/* check the CRC, and return if CRC is not correct */
	eh_crc = be32_to_cpu(ec_hdr.hdr_crc);
	if (eh_crc != mtd_crc32(UBI_CRC32_INIT, &ec_hdr, UBI_EC_HDR_SIZE_CRC)) {
		errmsg("EC header CRC check failed!\n");
		goto err_out;
	}
	
	/**
	 * get the vid_hdr_offset & data_offset, also the imge_seq
	 * they're stored in ec_hdr
	 */
	eh_vh_offset = be32_to_cpu(ec_hdr.vid_hdr_offset);
	eh_data_offset = be32_to_cpu(ec_hdr.data_offset);
	eh_image_seq = be32_to_cpu(ec_hdr.image_seq);

	/* store them in the global variant */
	peb_vh_offset = eh_vh_offset;
	peb_data_offset = eh_data_offset;
	image_seq = eh_image_seq;

	/**
	 * calculate the min_io_size
	 * guess the subpage_size
	 * vid_header will be the next subpage or nand page
	 * data will be the next nand page of vid_header
	 */
	/* the min_io_size and subpage_size are not needed here */

	/**
	 * read the vid_header
	 * check the crc
	 * get the data_size, data_pad, vol_id, lnum
	 */
	current_offs = SetFilePointer(h_ubiimg, eh_vh_offset, NULL, FILE_BEGIN);
	if (current_offs == INVALID_SET_FILE_POINTER) {
		errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
		goto err_out;
	}
	if (!ReadFile(h_ubiimg, &vid_hdr, UBI_VID_HDR_SIZE, &read_len, NULL)
		|| read_len != UBI_VID_HDR_SIZE)
	{
		errmsg("Read %s vid header failed!: %s\n", ubi_image_file,
			last_error(msg_buf, BUF_SIZE));
		goto err_out;
	}

	/* Check the magic & version values */
	if (UBI_VID_HDR_MAGIC != be32_to_cpu(vid_hdr.magic)) {
		errmsg("VID header MAGIC is not correct!\n");
		goto err_out;
	}
	if (vid_hdr.version > UBI_VERSION) {
		errmsg("can only support images under version %d!\n", UBI_VERSION);
		goto err_out;
	}
	/* Check the header crc */
	vh_crc = be32_to_cpu(vid_hdr.hdr_crc);
	if (vh_crc != mtd_crc32(UBI_CRC32_INIT, &vid_hdr, UBI_VID_HDR_SIZE_CRC)) {
		errmsg("VID header CRC check failed!\n");
		goto err_out;
	}

	vh_vol_id = be32_to_cpu(vid_hdr.vol_id);
	vh_lnum = be32_to_cpu(vid_hdr.lnum);
	vh_data_size = be32_to_cpu(vid_hdr.data_size);
	vh_used_ebs = be32_to_cpu(vid_hdr.used_ebs);
	vh_data_pad = be32_to_cpu(vid_hdr.data_pad);
	vh_data_crc = be32_to_cpu(vid_hdr.data_crc);
	vh_sqnum = be64_to_cpu(vid_hdr.sqnum);
	
	/**
	 * Check the volume information to ensure that it's the layout volume
	 */
	if (vh_vol_id != UBI_LAYOUT_VOLUME_ID) {
		errmsg("The first PEB has a wrong id for volume table!\n");
		goto err_out;
	}
	if (vid_hdr.vol_type != UBI_LAYOUT_VOLUME_TYPE) {
		errmsg("The first PEB has a wrong type for volume table!\n");
		goto err_out;
	}
	if (vh_lnum != 0) {
		errmsg("The first PEB has a wrong logical number for volume table!\n");
		goto err_out;
	}
	if (vid_hdr.compat != UBI_LAYOUT_VOLUME_COMPAT) {
		errmsg("The first PEB has a wrong compat flag for volume table!\n");
		goto err_out;
	}
	/**
	 * if the vid header passed all the checks above,
	 * it should almost be the layout vloume.
	 */
	/* scan the volume table */
	current_offs = SetFilePointer(h_ubiimg, eh_data_offset, NULL, FILE_BEGIN);
	if (current_offs == INVALID_SET_FILE_POINTER) {
		errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
		goto err_out;
	}
	vi_list_head->next = NULL;
	vi_node = vi_list_head;
	for (idx = 0; idx < UBI_MAX_VOLUMES; idx++) {
		if (!ReadFile(h_ubiimg, &vtbl_rec, UBI_VTBL_RECORD_SIZE, &read_len, NULL)
			|| read_len != UBI_VTBL_RECORD_SIZE)
		{
			errmsg("Read %s vid header failed!: %s\n", ubi_image_file,
				last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		vr_reserved_pebs = be32_to_cpu(vtbl_rec.reserved_pebs);
		/**
		 * if the value of reserved_pebs is zero,
		 * the record may be not a valid volume record,
		 * so skip it.
		 */
		if (vr_reserved_pebs == 0 || vr_reserved_pebs == 0xFFFFFFFFU) {
			continue;
		}
		/* check the crc */
		vr_crc = be32_to_cpu(vtbl_rec.crc);
		if (vr_crc != mtd_crc32(UBI_CRC32_INIT, &vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC)) {
			//errmsg("volume table record CRC check failed!\n");
			continue;
		}
		vr_alignment = be32_to_cpu(vtbl_rec.alignment);
		vr_data_pad = be32_to_cpu(vtbl_rec.data_pad);
		vr_name_len = be16_to_cpu(vtbl_rec.name_len);

		/* add this vtbl rec to the volume info list */
		vi_node->next = (struct volume_info_node *)calloc(1, sizeof(struct volume_info_node));
		if (vi_node->next == NULL) {
			errmsg("Failed to allocate memory!\n");
			goto err_out1;
		}
		vi_node = vi_node->next;
		vi_node->leb_count = 0;
		vi_node->leb_list_head = NULL;
		vi_node->next = NULL;
		vi_node->vol_id = idx;
		vi_node->vol_type = vtbl_rec.vol_type;
		if (vi_node->vol_type != UBI_VID_STATIC && vi_node->vol_type != UBI_VID_DYNAMIC) {
			errmsg("unkown volume type: %d\n", vi_node->vol_type);
			goto err_out1;
		}
		vi_node->alignment = vr_alignment;
		vi_node->reserved_pebs = vr_reserved_pebs;
		vi_node->flags = vtbl_rec.flags;
		if (vr_name_len <= 0 || vr_name_len > UBI_VOL_NAME_MAX) {
			errmsg("got a volume without name or name invalid!!\n");
			goto err_out1;
		}
		vi_node->name = (char *)malloc(vr_name_len + 1);
		if (vi_node->name == NULL) {
			errmsg("Failed to allocate memory!\n");
			goto err_out1;
		}
		memcpy(vi_node->name, vtbl_rec.name, vr_name_len);
		vi_node->name_len = vr_name_len;
		vi_node->name[vi_node->name_len] = '\0';
		/**
		 * may be need in the future
		if (vr_data_pad != vh_data_pad) {
			warnmsg("Conflict data pad in the volume table record: %d\n", idx);
		}
		*/
		vi_node->data_pad = vr_data_pad;
		/* rememer the id as the current max id */
		max_vol_id = idx;
		/* get a temporary file to store the image */
		ret = GetTempPath(BUF_SIZE, buf);
		if (ret > BUF_SIZE || (ret <= 0)) {
			errmsg("GetTempPath failed with error %s.\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		sprintf(msg_buf, "ubi_vol%d_", idx);
		ret = GetTempFileName(buf, msg_buf, 0, temp_file);
		if (ret == 0) {
			printf ("GetTempFileName failed with error %s.\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		vi_node->image_path = _strdup(temp_file);
	}
	
	/**
	 * now we need to get the peb size before doing anything else
	 * the are two ways to guess the peb size:
	 * 1. read the first peb untill we get a valid ec header,
	 *    that's the start of the second peb.
	 * 2. because the size of a pebs must be the power of 2,
	 *    so we can read from the first power of 2 after vid header to 2^n,
	 *    untill we get a valid ec header, or reach the end of the file.
	 * we choose the second way.
	 */
	/* we start our read after the position of max volume record */
	current_offs = eh_data_offset + max_vol_id * UBI_VTBL_RECORD_SIZE;
	peb_size = MIN_PEB_SIZE;
	/* get next position which is power of 2 */
	while (current_offs > peb_size) {
		peb_size <<= 1;
	}
	for (;peb_size <= MAX_PEB_SIZE; peb_size <<= 1) {
		current_offs = SetFilePointer(h_ubiimg, peb_size, NULL, FILE_BEGIN);
		if (current_offs == INVALID_SET_FILE_POINTER) {
			errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		if (!ReadFile(h_ubiimg, &ec_hdr, UBI_EC_HDR_SIZE, &read_len, NULL)
			|| read_len != UBI_EC_HDR_SIZE)
		{
			errmsg("Read %s ec header failed!: %s\n", ubi_image_file,
				last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		/* Check the magic values */
		if (UBI_EC_HDR_MAGIC != be32_to_cpu(ec_hdr.magic)) {
			continue;
		}
		/**
		 * if the magic is correct, we almost get the next peb,
		 * but we still use crc to check
		 */
		eh_crc = be32_to_cpu(ec_hdr.hdr_crc);
		if (eh_crc != mtd_crc32(UBI_CRC32_INIT, &ec_hdr, UBI_EC_HDR_SIZE_CRC)) {
			continue;
		}

		/* check the vid header offset, data offset, and image_seq */
		eh_vh_offset = be32_to_cpu(ec_hdr.vid_hdr_offset);
		eh_data_offset = be32_to_cpu(ec_hdr.data_offset);
		eh_image_seq = be32_to_cpu(ec_hdr.image_seq);
		if (eh_vh_offset != peb_vh_offset) {
			errmsg("not all the pebs have the same vid header offset\n");
			goto err_out1;
		}
		if (eh_data_offset != peb_data_offset) {
			errmsg("not all the pebs have the same data offset\n");
			goto err_out1;
		}
		if (eh_image_seq != image_seq) {
			errmsg("not all the pebs have the same image_seq\n");
			goto err_out1;
		}

		/* now we are in the second peb */
		break;
	}
	if (peb_size > MAX_PEB_SIZE) {
		errmsg("can't get the peb size!\n");
		goto err_out1;
	}
	
	/* check if the peb size is correct through the file size */
	file_size = GetFileSize(h_ubiimg, NULL);
	if (file_size == INVALID_FILE_SIZE) {
		errmsg("Failed to get the file size: %s\n", last_error(msg_buf, BUF_SIZE));
		goto err_out1;
	}
	if (file_size % peb_size != 0) {
		errmsg("Wrong image size or non-standard image.\n");
		goto err_out1;
	}

	/**
	 * the second peb stores the same content of the first peb,
	 * and we will check that to ensure the peb size is correct.
	 */
	current_peb_offs = peb_size;
	/**
	 * read the vid_header
	 * check the crc
	 * get the data_size, data_pad, vol_id, lnum
	 */
	current_offs = SetFilePointer(h_ubiimg, current_peb_offs + eh_vh_offset, NULL, FILE_BEGIN);
	if (current_offs == INVALID_SET_FILE_POINTER) {
		errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
		goto err_out1;
	}
	if (!ReadFile(h_ubiimg, &vid_hdr, UBI_VID_HDR_SIZE, &read_len, NULL)
		|| read_len != UBI_VID_HDR_SIZE)
	{
		errmsg("Read %s vid header failed!: %s\n", ubi_image_file,
			last_error(msg_buf, BUF_SIZE));
		goto err_out1;
	}

	/* Check the magic & version values */
	if (UBI_VID_HDR_MAGIC != be32_to_cpu(vid_hdr.magic)) {
		errmsg("VID header MAGIC is not correct!\n");
		goto err_out1;
	}
	if (vid_hdr.version > UBI_VERSION) {
		errmsg("can only support images under version %d\n", UBI_VERSION);
		goto err_out1;
	}
	/* Check the header crc */
	vh_crc = be32_to_cpu(vid_hdr.hdr_crc);
	if (vh_crc != mtd_crc32(UBI_CRC32_INIT, &vid_hdr, UBI_VID_HDR_SIZE_CRC)) {
		errmsg("VID header CRC check failed!\n");
		goto err_out1;
	}

	vh_vol_id = be32_to_cpu(vid_hdr.vol_id);
	vh_lnum = be32_to_cpu(vid_hdr.lnum);
	
	/**
	 * Check the volume information to ensure that it's the layout volume
	 */
	if (vh_vol_id != UBI_LAYOUT_VOLUME_ID) {
		errmsg("The second PEB has a wrong id for volume table!\n");
		goto err_out1;
	}
	if (vid_hdr.vol_type != UBI_LAYOUT_VOLUME_TYPE) {
		errmsg("The second PEB has a wrong type for volume table!\n");
		goto err_out1;
	}
	if (vh_lnum != 1) {
		errmsg("The second PEB has a wrong logical number for volume table!\n");
		goto err_out1;
	}
	if (vid_hdr.compat != UBI_LAYOUT_VOLUME_COMPAT) {
		errmsg("The second PEB has a wrong compat flag for volume table!\n");
		goto err_out1;
	}
	/**
	 * if the vid header passed all the checks above,
	 * it should almost be the layout vloume.
	 */
	/* scan the volume table */
	current_offs = SetFilePointer(h_ubiimg, current_peb_offs + eh_data_offset, NULL, FILE_BEGIN);
	if (current_offs == INVALID_SET_FILE_POINTER) {
		errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
		goto err_out1;
	}
	for (idx = 0; idx <= max_vol_id; idx++) {
		if (!ReadFile(h_ubiimg, &vtbl_rec, UBI_VTBL_RECORD_SIZE, &read_len, NULL)
			|| read_len != UBI_VTBL_RECORD_SIZE)
		{
			errmsg("Read %s vid header failed!: %s\n", ubi_image_file,
				last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		vr_reserved_pebs = be32_to_cpu(vtbl_rec.reserved_pebs);
		/**
		 * if the value of reserved_pebs is zero,
		 * the record may be not a valid volume record,
		 * so skip it.
		 */
		if (vr_reserved_pebs == 0 || vr_reserved_pebs == 0xFFFFFFFFU) {
			continue;
		}
		/* check the crc */
		vr_crc = be32_to_cpu(vtbl_rec.crc);
		if (vr_crc != mtd_crc32(UBI_CRC32_INIT, &vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC)) {
			//errmsg("volume table record CRC check failed!\n");
			continue;
		}

		vr_alignment = be32_to_cpu(vtbl_rec.alignment);
		vr_data_pad = be32_to_cpu(vtbl_rec.data_pad);
		vr_name_len = be16_to_cpu(vtbl_rec.name_len);

		/**
		 * traverse the volume info list
		 * the manner of the traverse is the simplest type 
		 */
		vi_node = vi_list_head->next;
		while (vi_node != NULL) {
			if (vi_node->vol_id == idx) {
				if (vtbl_rec.vol_type != vi_node->vol_type) {
					errmsg("the volume: %d in first peb and second peb have different types!\n", idx);
					goto err_out1;
				}
				if (vr_reserved_pebs != vi_node->reserved_pebs) {
					errmsg("the volume: %d in first peb and second peb have different reserved_pebs!\n", idx);
					goto err_out1;
				}
				if (vr_alignment != vi_node->alignment) {
					errmsg("the volume: %d in first peb and second peb have different alignments!\n", idx);
					goto err_out1;
				}
				if (vr_data_pad != vi_node->data_pad) {
					errmsg("the volume: %d in first peb and second peb have different data_pads!\n", idx);
					goto err_out1;
				}
				if (vr_name_len != vi_node->name_len) {
					errmsg("the volume: %d in first peb and second peb have different name_lens!\n", idx);
					goto err_out1;
				}
				if (vtbl_rec.flags != vi_node->flags) {
					errmsg("the volume: %d in first peb and second peb have different flags!\n", idx);
					goto err_out1;
				}
			}
			vi_node = vi_node->next;
		}
	}

	/* now we have checked the second peb, and the peb_size must be correct */

	leb_size = peb_size - peb_data_offset;


	/**
	 * scan all the pebs, and build a list of volumes by lnum
	 * we can't assume that all the lebs of a volume are consequent
	 */
	current_peb_offs += peb_size;
	while (current_peb_offs < file_size) {
		current_offs = SetFilePointer(h_ubiimg, current_peb_offs, NULL, FILE_BEGIN);
		if (current_offs == INVALID_SET_FILE_POINTER) {
			errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		if (!ReadFile(h_ubiimg, &ec_hdr, UBI_EC_HDR_SIZE, &read_len, NULL)
			|| read_len != UBI_EC_HDR_SIZE)
		{
			errmsg("Read %s ec header failed!: %s\n", ubi_image_file,
				last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		/* Check the magic & version values */
		if (UBI_EC_HDR_MAGIC != be32_to_cpu(ec_hdr.magic)) {
			errmsg("EC header MAGIC is not correct!\n");
			goto err_out1;
		}
		if (ec_hdr.version > UBI_VERSION) {
			errmsg("can only support images under version %d!\n", UBI_VERSION);
			goto err_out1;
		}
		/* check the CRC */
		eh_crc = be32_to_cpu(ec_hdr.hdr_crc);
		if (eh_crc != mtd_crc32(UBI_CRC32_INIT, &ec_hdr, UBI_EC_HDR_SIZE_CRC)) {
			errmsg("EC header CRC check failed!\n");
			goto err_out1;
		}
				
		/* check the vid header offset, data offset, and image_seq */
		eh_vh_offset = be32_to_cpu(ec_hdr.vid_hdr_offset);
		eh_data_offset = be32_to_cpu(ec_hdr.data_offset);
		eh_image_seq = be32_to_cpu(ec_hdr.image_seq);
		if (eh_vh_offset != peb_vh_offset) {
			errmsg("not all the pebs have the same vid header offset\n");
			goto err_out1;
		}
		if (eh_data_offset != peb_data_offset) {
			errmsg("not all the pebs have the same data offset\n");
			goto err_out1;
		}
		if (eh_image_seq != image_seq) {
			errmsg("not all the pebs have the same image_seq\n");
			goto err_out1;
		}

		/**
		* read the vid_header
		* check the crc
		* get the data_size, data_pad, vol_id, lnum
		*/
		current_offs = SetFilePointer(h_ubiimg, current_peb_offs + eh_vh_offset, NULL, FILE_BEGIN);
		if (current_offs == INVALID_SET_FILE_POINTER) {
			errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
		if (!ReadFile(h_ubiimg, &vid_hdr, UBI_VID_HDR_SIZE, &read_len, NULL)
			|| read_len != UBI_VID_HDR_SIZE)
		{
			errmsg("Read %s vid header failed!: %s\n", ubi_image_file,
				last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}

		/* Check the magic & version values */
		if (UBI_VID_HDR_MAGIC != be32_to_cpu(vid_hdr.magic)) {
			errmsg("VID header MAGIC is not correct!\n");
			goto err_out1;
		}
		if (vid_hdr.version > UBI_VERSION) {
			errmsg("can only support images under version %d!\n", UBI_VERSION);
			goto err_out1;
		}
		/* Check the header crc */
		vh_crc = be32_to_cpu(vid_hdr.hdr_crc);
		if (vh_crc != mtd_crc32(UBI_CRC32_INIT, &vid_hdr, UBI_VID_HDR_SIZE_CRC)) {
			errmsg("VID header CRC check failed!\n");
			goto err_out1;
		}

		vh_vol_id = be32_to_cpu(vid_hdr.vol_id);
		vh_lnum = be32_to_cpu(vid_hdr.lnum);
		vh_data_size = be32_to_cpu(vid_hdr.data_size);
		vh_used_ebs = be32_to_cpu(vid_hdr.used_ebs);
		vh_data_pad = be32_to_cpu(vid_hdr.data_pad);
		vh_data_crc = be32_to_cpu(vid_hdr.data_crc);
		vh_sqnum = be32_to_cpu(vid_hdr.sqnum);

		/* find the corresponding volume info node */
		if (!(vi_node && vi_node->vol_id == vh_vol_id)) {
			vi_node = vi_list_head->next;
			while (vi_node != NULL) {
				if (vi_node->vol_id == vh_vol_id)
					break;
				vi_node = vi_node->next;
			}
		}
		/* if the vol id is not in the list */
		if (vi_node == NULL) {
			errmsg("The volume id in this pebs doesn't exist in the volume table!\n");
			goto err_out1;
		}
		/* add a leb list node and remember the data's offset and length */
		leb_node = (struct leb_list *)calloc(1, sizeof(struct leb_list));
		if (leb_node == NULL) {
			errmsg("Failed to allocate memory!\n");
			goto err_out1;
		}
		leb_node->leb_num = vh_lnum;
		leb_node->data_offset = current_peb_offs + eh_data_offset;
		switch (vid_hdr.vol_type) {
		case UBI_VID_DYNAMIC:
			leb_node->data_size = leb_size - vh_data_pad;
			break;
		case UBI_VID_STATIC:
			leb_node->data_size = vh_data_size;
			break;
		}
		
		/**
		 * insert into the list
		 * because we will always get a bigger lnum,
		 * so it's beterr to insert the node at the tail
		 * but we still need to check if we get a smaller lnum
		 */
		if (vi_node->leb_list_head == NULL) {
			leb_node->last = leb_node;
			leb_node->next = leb_node;
			vi_node->leb_list_head = leb_node;
		} else {
			struct leb_list *tmp_node = vi_node->leb_list_head->last;
			while (tmp_node != vi_node->leb_list_head 
				&& tmp_node->leb_num > leb_node->leb_num)
			{
				tmp_node = tmp_node->last;
			}
			if (tmp_node->leb_num > leb_node->leb_num) {
				leb_node->next = tmp_node;
				leb_node->last = tmp_node->last;
				tmp_node->last->next = leb_node;
				tmp_node->last = leb_node;
				vi_node->leb_list_head = leb_node;
			} else {
				leb_node->last = tmp_node;
				leb_node->next = tmp_node->next;
				tmp_node->next->last = leb_node;
				tmp_node->next = leb_node;
			}
		}
		vi_node->leb_count++;
		/* read next peb */
		current_peb_offs += peb_size;
	}
	
	/* we only read the first volume now */
	vi_node = vi_list_head->next;
	if (vi_node == NULL) {
		errmsg("No volume was found!\n");
		goto err_out1;
	}
	/* read super block in leb0 */
	leb_node = vi_node->leb_list_head;
	if (leb_node == NULL) {
		errmsg("No leb node was found!\n");
		goto err_out1;
	}

	/* read the leb 0, it must be the first leb */
	if (leb_node->leb_num == 0) {
		struct ubifs_sb_node sup_node;
		struct ubifs_sb_node *sup = &sup_node;
		uint32_t ch_magic;
		uint32_t ch_len;
		uint32_t ch_crc;
		uint32_t sup_flags;

		struct ubifs_ch *ch = (struct ubifs_ch *)sup;

		current_offs = SetFilePointer(h_ubiimg, leb_node->data_offset, NULL, FILE_BEGIN);
		if (current_offs == INVALID_SET_FILE_POINTER) {
			errmsg("SetFilePointer failed: %s\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}

		/**
		 * Note: the values read out are little-endian
		 */
		if (!ReadFile(h_ubiimg, sup, UBIFS_SB_NODE_SZ, &read_len, NULL) || read_len != UBIFS_SB_NODE_SZ) {
			errmsg("Read file failed!: %s\n", last_error(msg_buf, BUF_SIZE));
			goto err_out1;
		}
	
		ch_magic = le32_to_cpu(ch->magic);
		if (ch_magic != UBIFS_NODE_MAGIC) {
			errmsg("Node's common header MAGIC is not correct!\n");
			goto err_out1;
		}

		ch_len = le32_to_cpu(ch->len);
		if (ch_len > UBIFS_SB_NODE_SZ) {
			errmsg("Incorrect node size!\n");
			goto err_out1;
		}

		ch_crc = le32_to_cpu(ch->crc);
		if (ch_crc != mtd_crc32(UBIFS_CRC32_INIT, (uint8_t *)sup + 8, ch_len - 8)) {
			errmsg("CRC check failed!\n");
			goto err_out1;
		}
		if (sup->ch.node_type != UBIFS_SB_NODE) {
			errmsg("The node type of super block is not UBIFS_SB_NODE!\n");
			goto err_out1;
		}

		switch (sup->key_hash) {
		case UBIFS_KEY_HASH_R5:
			c->key_hash = key_r5_hash;
			c->key_hash_type = UBIFS_KEY_HASH_R5;
			break;

		case UBIFS_KEY_HASH_TEST:
			c->key_hash = key_test_hash;
			c->key_hash_type = UBIFS_KEY_HASH_TEST;
			break;
		};

		c->key_fmt = sup->key_fmt;

		switch (c->key_fmt) {
		case UBIFS_SIMPLE_KEY_FMT:
			c->key_len = UBIFS_SK_LEN;
			break;
		default:
			errmsg("Unsupported key format\n");
			goto err_out1;
		}
	
		c->min_io_size   = le32_to_cpu(sup->min_io_size);
		c->leb_size      = le32_to_cpu(sup->leb_size);
		c->leb_cnt       = le32_to_cpu(sup->leb_cnt);
		c->max_leb_cnt   = le32_to_cpu(sup->max_leb_cnt);
		c->max_bud_bytes = le64_to_cpu(sup->max_bud_bytes);
		c->log_lebs      = le32_to_cpu(sup->log_lebs);
		c->lpt_lebs      = le32_to_cpu(sup->lpt_lebs);
		c->orph_lebs     = le32_to_cpu(sup->orph_lebs);
		c->jhead_cnt     = le32_to_cpu(sup->jhead_cnt);
		c->fanout        = le32_to_cpu(sup->fanout);
		c->lsave_cnt     = le32_to_cpu(sup->lsave_cnt);
		c->rp_size       = le64_to_cpu(sup->rp_size);
		//c->rp_uid        = make_kuid(&init_user_ns, le32_to_cpu(sup->rp_uid));
		//c->rp_gid        = make_kgid(&init_user_ns, le32_to_cpu(sup->rp_gid));
		sup_flags        = le32_to_cpu(sup->flags);
		c->default_compr = le16_to_cpu(sup->default_compr);
		c->big_lpt       = !!(sup_flags & UBIFS_FLG_BIGLPT);
		c->space_fixup   = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
		c->lpt_first     = UBIFS_LOG_LNUM + c->log_lebs;
		c->lpt_last      = c->lpt_first + c->lpt_lebs - 1;
		c->main_lebs     = c->max_leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS;
		c->main_lebs    -= c->log_lebs + c->lpt_lebs + c->orph_lebs;
		c->main_first    = c->max_leb_cnt - c->main_lebs;
	
		if (validate_sb(c, sup) != 0)
			goto err_out1;
	} else {
		errmsg("can't fint leb 0!\n");
		goto err_out1;
	}
	
	u->peb_size = peb_size;
	u->leb_size = c->leb_size;
	u->min_io_size = c->min_io_size;
	u->vid_hdr_offs = peb_vh_offset;
	u->data_offs = peb_data_offset;
	u->ubi_ver = UBI_VERSION;
	//u->image_seq = image_seq;
	//u->vtbl_size = ?;
	//u->max_volumes = max_vol_id;
	
	ubi_srand();
	u->image_seq = rand();

	if (check_ubi_args(u) !=0)
		goto err_out1;
	
	ret_val = 0;

err_out1:
	/* we don't delete it currently if no error*/
	if (ret_val != 0) {
		free_vi_list(vi_list_head);
	}
err_out:
	CloseHandle(h_ubiimg);
	return ret_val;
}

int get_img_type(const char *in_image_file)
{
	HANDLE h_img_file;
	struct ubi_ec_hdr ec_hdr;  // 160 bytes
	DWORD read_len;
	struct ubifs_ch *ch;	// 24 bytes
	int type = -1;

	/* read the first 64 bytes for ec_header */
	h_img_file = CreateFile(in_image_file, GENERIC_READ, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (h_img_file == INVALID_HANDLE_VALUE) {
		errmsg("Open %s failed!: %s\n", in_image_file, last_error(msg_buf, BUF_SIZE));
		return -1;
	}
	
	// check_ubi
	/**
	 * Note: the values read out are big-endian
	 */
	if (!ReadFile(h_img_file, &ec_hdr, UBI_EC_HDR_SIZE, &read_len, NULL)
		|| read_len != UBI_EC_HDR_SIZE)
	{
		errmsg("Read %s ec header failed!: %s\n", in_image_file,
			last_error(msg_buf, BUF_SIZE));
		goto err_out;
	}
	
	/* Check the magic & version values */
	if (UBI_EC_HDR_MAGIC == be32_to_cpu(ec_hdr.magic)
			&& ec_hdr.version <= UBI_VERSION) {
		type = 1;
		//printf("type = 1");
		goto err_out;
	}

	//check_ubifs
	/**
	 * we don't need to read file again , but need to note that 
	 * the values read out are little-endian
	 */
	ch = (struct ubifs_ch *)&ec_hdr;
	if (le32_to_cpu(ch->magic) == UBIFS_NODE_MAGIC) {
		type = 2;
		//printf("type = 2");
		goto err_out;
	}

	errmsg("can't get the type of image!\n");

err_out:
	CloseHandle(h_img_file);
	return type;
}